package net.sk.china.platform.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import net.sk.china.platform.constants.Constants;
import net.sk.china.platform.model.system.SystemAdmin;
import net.sk.china.platform.model.system.SystemMenu;
import net.sk.china.platform.request.AdminLoginRequest;
import net.sk.china.platform.request.ValidateCodeRequest;
import net.sk.china.platform.request.system.AdminResponse;
import net.sk.china.platform.response.system.LoginResponse;
import net.sk.china.platform.response.system.LoginSystemResponse;
import net.sk.china.platform.response.system.SystemGroupDataAdminLoginBannerResponse;
import net.sk.china.platform.response.system.SystemPermissionsResponse;
import net.sk.china.platform.service.AdminService;
import net.sk.china.platform.service.system.SystemAdminService;
import net.sk.china.platform.service.system.SystemConfigService;
import net.sk.china.platform.service.system.SystemGroupDataService;
import net.sk.china.platform.service.system.SystemMenuService;
import net.sk.china.platform.token.TokenComponent;
import net.sk.china.platform.utils.MenuTreeUtil;
import net.sk.china.platform.utils.MenusResponse;
import net.sk.china.platform.utils.ValidateCodeUtil;
import net.sk.china.common.exception.TalkException;
import net.sk.china.common.utils.RedisUtil;
import net.sk.china.common.utils.TalkUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * AdminServiceImpl 接口实现
 * +----------------------------------------------------------------------
 * // | Talk To [ 聊来改善地区金融服务环境，助力企业发展 ]
 * // +----------------------------------------------------------------------
 * // | Copyright (c) 2018-2024 聊来 All rights reserved.
 * // +----------------------------------------------------------------------
 * // | Licensed ( <a href="http://www.apache.org/licenses/LICENSE-2.0">apache</a> )
 * // +----------------------------------------------------------------------
 * // | @Author: 聊来 <18970881148@qq.com>
 * // +----------------------------------------------------------------------
 * // | DateTime: 2024/6/3 10:02
 * // +----------------------------------------------------------------------
 */

@Service
public class AdminServiceImpl implements AdminService {

    private final TokenComponent tokenComponent;

    private final RedisUtil redisUtil;

    private final AuthenticationManager authenticationManager;

    private final SystemAdminService systemAdminService;

    private final SystemMenuService systemMenuService;

    private final SystemConfigService systemConfigService;

    private final SystemGroupDataService systemGroupDataService;

    public AdminServiceImpl(TokenComponent tokenComponent, RedisUtil redisUtil, AuthenticationManager authenticationManager, SystemAdminService systemAdminService, SystemMenuService systemMenuService, SystemConfigService systemConfigService, SystemGroupDataService systemGroupDataService) {
        this.tokenComponent = tokenComponent;
        this.redisUtil = redisUtil;
        this.authenticationManager = authenticationManager;
        this.systemAdminService = systemAdminService;
        this.systemMenuService = systemMenuService;
        this.systemConfigService = systemConfigService;
        this.systemGroupDataService = systemGroupDataService;
    }

    /**
     * 登录
     * @param request 请求数据
     * @param ip 登录IP
     * @return {@code LoginResponse}
     */
    @Override
    public LoginResponse login(AdminLoginRequest request, String ip) {
        boolean checkCode = checkValidateCode(request.getKey(), request.getCode());
        if (!checkCode) {
            throw new TalkException("验证码不正确");
        }

        // 用户验证
        Authentication authentication;
        try {
            authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(request.getAccount(), request.getPwd()));
        } catch (AuthenticationException e) {
            if (e instanceof BadCredentialsException) {
                throw new TalkException("用户不存在或密码错误");
            }
            throw new TalkException(e.getMessage());
        }
        LoginSystemResponse loginSystemResponse = (LoginSystemResponse) authentication.getPrincipal();
        SystemAdmin systemAdmin = loginSystemResponse.getUser();

        String token = tokenComponent.createToken(loginSystemResponse);

        LoginResponse response = new LoginResponse();
        response.setToken(token);
        BeanUtils.copyProperties(systemAdmin, response);

        // 更新登录信息
        systemAdmin.setLoginCount(systemAdmin.getLoginCount() + 1);
        systemAdmin.setLastIp(ip);
        systemAdminService.updateById(systemAdmin);

        return response;
    }

    /**
     * 获取登录相关数据
     * @return {@code Map<String, Object>}
     */
    @Override
    public Map<String, Object> getLoginPic() {
        Map<String, Object> map = new HashMap<>(8);
        //背景图
        map.put("backgroundImage", systemConfigService.getValueByKey(Constants.CONFIG_KEY_ADMIN_LOGIN_BACKGROUND_IMAGE));
        //logo
        map.put("logo", systemConfigService.getValueByKey(Constants.CONFIG_KEY_ADMIN_LOGIN_LOGO_LEFT_TOP));
        map.put("loginLogo", systemConfigService.getValueByKey(Constants.CONFIG_KEY_ADMIN_LOGIN_LOGO_LOGIN));
        //轮播图
        List<SystemGroupDataAdminLoginBannerResponse> bannerList = systemGroupDataService.getListByGid(Constants.GROUP_DATA_ID_ADMIN_LOGIN_BANNER_IMAGE_LIST, SystemGroupDataAdminLoginBannerResponse.class);
        map.put("banner", bannerList);

        return map;
    }

    /**
     * 获取图片验证码
     * @return {@code ValidateCodeRequest}
     */
    @Override
    public ValidateCodeRequest getValidateCode() {
        ValidateCodeUtil.Validate randomCode = ValidateCodeUtil.getRandomCode();
        if (ObjectUtil.isNull(randomCode)) { return null; }

        String value = randomCode.getValue().toLowerCase();
        String md5Key = SecureUtil.md5(value);
        String redisKey = getRedisKey(md5Key);
        // 设置5分钟后过期
        redisUtil.set(redisKey, value, 5L, TimeUnit.MINUTES);


        String base64Str = randomCode.getBase64Str();

        return new ValidateCodeRequest(md5Key, TalkUtil.getBase64Image(base64Str));
    }

    /**
     * 退出登录
     * @return {@code boolean}
     */
    @Override
    public boolean logout() {
        LoginSystemResponse vo = getLoginAdminResponse();
        tokenComponent.delLoginUser(vo.getToken());
        return true;
    }

    /**
     * 根据Token获取对应用户信息
     * @return {@code AdminResponse}
     */
    @Override
    public AdminResponse getInfoByToken() {
        LoginSystemResponse loginSystemResponse = getLoginAdminResponse();
        SystemAdmin systemAdmin = loginSystemResponse.getUser();
        AdminResponse adminResponse = new AdminResponse();
        BeanUtils.copyProperties(systemAdmin, adminResponse);
        List<String> roleList = Stream.of(systemAdmin.getRoles().split(",")).collect(Collectors.toList());
        List<String> permList = CollUtil.newArrayList();
        if (roleList.contains("1")) {
            permList.add("*:*:*");
        } else {
            permList = loginSystemResponse.getPermissions().stream().map(SystemPermissionsResponse::getPath).collect(Collectors.toList());
        }
        adminResponse.setPermissionsList(permList);

        return adminResponse;
    }

    /**
     * 获取管理员可访问目录
     * @return {@code List<MenusResponse>}
     */
    @Override
    public List<MenusResponse> getMenus() {
        LoginSystemResponse loginSystemResponse = getLoginAdminResponse();
        List<String> roleList = Stream.of(loginSystemResponse.getUser().getRoles().split(",")).collect(Collectors.toList());
        List<SystemMenu> menuList;
        // 超级管理员
        String administrator = "1";
        if (roleList.contains(administrator)) {
            menuList = systemMenuService.findAllCatalogue();
        } else {
            menuList = systemMenuService.getMenusByUserId(loginSystemResponse.getUser().getId());
        }
        // 组装前端对象
        List<MenusResponse> responseList = menuList.stream().map(e -> {
            MenusResponse response = new MenusResponse();
            BeanUtils.copyProperties(e, response);
            return response;
        }).collect(Collectors.toList());


        MenuTreeUtil menuTree = new MenuTreeUtil(responseList);
        return menuTree.buildTree();
    }

    /**
     * 获取管理员信息（从security中）
     * @return {@code LoginSystemResponse}
     */
    private LoginSystemResponse getLoginAdminResponse() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginSystemResponse loginSystemResponse = (LoginSystemResponse) authentication.getPrincipal();
        if (ObjectUtil.isNull(loginSystemResponse)) {
            throw new TalkException("登录信息已过期，请重新登录");
        }
        return loginSystemResponse;
    }

    /**
     * 确认验证码
     * @param key md5加密
     * @param code 验证码
     * @return {@code boolean}
     */
    private boolean checkValidateCode(String key, String code)
    {
        Object redisValue = redisUtil.get(getRedisKey(key));
        if (!redisUtil.exists(getRedisKey(key)) || ObjectUtil.isNull(redisValue)) {
            throw new TalkException("验证码KEY错误");
        }

        return redisValue.equals(code.toLowerCase());
    }

    /**
     * 获取redis key
     * @param md5Key value的md5加密值
     * @return {@code String}
     */
    private String getRedisKey(String md5Key) {
        return Constants.VALIDATE_REDIS_KEY_PREFIX + md5Key;
    }
}
